
#include "audio_synth.h"
#include <math.h>

using namespace WtfEngine;
using namespace WtfAudio;



PcmOutput::PcmOutputCreationOptions::PcmOutputCreationOptions()
: DataFormat(Data::AUDIODATA_MONO_16), uBitRate(48000)
{
};

PcmOutput::PcmOutput(const WtfEngine::StringPool::Entry &sName, Data::tRefa rSrc,
	const PcmOutput::tCreationOptions& opts)
: Decorator(sName, rSrc)
{
	if(mrSource->getFormat() != AUDIODATA_INTERNAL)
		throw std::logic_error("PcmOutput only accepts internal format as source");

	mFormat = opts.DataFormat;
	mBitRate = opts.uBitRate;
};

bool PcmOutput::getData(const WtfEngine::NonCopyBuffer<>::tRef &rBuffer) {
	unsigned uSamples;

#	define PCM_COPY(Type) \
		uSamples = rBuffer->getCount() / sizeof(Type); \
		if(mrBuffer.isNull() || (mrBuffer->getCount() < uSamples * sizeof(tInternalFormat))) \
			mrBuffer = new NonCopyBuffer<>(uSamples * sizeof(tInternalFormat)); \
		if(!mrSource->getData(mrBuffer)) return false; \
		for(unsigned i = 0; i < uSamples; i++) { \
			((Type *)rBuffer->getData())[i] = Type( (((tInternalFormat *)mrBuffer->getData())[i] + 1.0) \
				* (tInternalFormat)(1 << ((sizeof(Type) * 8) - 2)) ); \
		}

	switch(mFormat) {
	case AUDIODATA_MONO_16:
		PCM_COPY(unsigned short);
		break;
	case AUDIODATA_MONO_8:
		PCM_COPY(unsigned char);
		break;
	default:
		throw std::logic_error("PcmOutput - unsupported output format");
	};

	return true;
#	undef PCM_COPY
};


/**** Wave Generator ****/

WaveGenerator::WaveGeneratorCreationOptions::WaveGeneratorCreationOptions()
: uOutputRate(6000 / sizeof(tInternalFormat)), rAmplitudeFunction(new SineWaveFunction(new ConstantFrequency(50.0)))
{
};


WaveGenerator::WaveGenerator(const WtfEngine::StringPool::Entry &sName, const WaveGenerator::tCreationOptions& opts)
: Generator(sName),mrAmplitudeFunction(opts.rAmplitudeFunction)
{
	mFormat = AUDIODATA_INTERNAL;
	mBitRate = opts.uOutputRate * sizeof(tInternalFormat) * 8;
	Reset();
};


/**
 * Generates enough data to fill the passed audio buffer (the amount of passed
 * time is determined from the size of the buffer)
 **/
bool WaveGenerator::getData(const WtfEngine::NonCopyBuffer<>::tRef &rBuffer)
{
	unsigned uOutputSamples = rBuffer->getCount() / sizeof(tInternalFormat);
	tScalar dt = (tScalar)(rBuffer->getCount() * 8) / (tScalar)mBitRate;
	tScalar ddt = dt / (tScalar)uOutputSamples;
	
	for(unsigned i = 0; i < uOutputSamples; i++) {
		((tInternalFormat *)rBuffer->getData())[i] = mrAmplitudeFunction->getValue(mTime, ddt);
		mTime += ddt;
	};

	return true;
};

void WaveGenerator::Reset()
{
	mTime = 0;
};


/**** Multiplexers ****/

Multiplexer::Multiplexer(const WtfEngine::StringPool::Entry& sName, Multiplexer::IType::tRefa rType)
: Generator(sName), mrType(rType)
{
	mBitRate = 48000;
	mFormat = AUDIODATA_INTERNAL;
};

bool Multiplexer::getData(WtfEngine::NonCopyBuffer<>::tRefa rBuffer)
{
	// Create buffers and fetch data
	// TODO: distinct bit rates
	mInputBuffers.resize(mSourceList.size());
	unsigned uInput = 0;
	for(tSourceList::const_iterator i = mSourceList.begin(); i != mSourceList.end(); i++) {
		if(mInputBuffers[uInput].first.isNull() || mInputBuffers[uInput].first->getCount() != rBuffer->getCount()) {
			mInputBuffers[uInput].first = new NonCopyBuffer<>(rBuffer->getCount());
		};
		mInputBuffers[uInput].second = (*i)->getBitRate() / (8 * sizeof(tInternalFormat));

		if(!(*i)->getData(mInputBuffers[uInput].first))
			return false;

		uInput++;
	};

	mrType->CombineStreams(rBuffer, mInputBuffers, rBuffer->getCount() / sizeof(tInternalFormat));
	return true;
};

void Multiplexer::Reset()
{
	for(tSourceList::const_iterator i = mSourceList.begin(); i != mSourceList.end(); i++) {
		(*i)->Reset();
	};
};

void Multiplexer::AddSource(WtfAudio::Generator::tRefa rStream)
{
	if(mSourceList.size() == 0) {
		mBitRate = rStream->getBitRate();
	} else {
		if(mBitRate != rStream->getBitRate())
			throw std::logic_error("Distinct bit rates cannot be multiplexed (not implemented)");
	};

	mSourceList.push_back(rStream);
};

void Multiplexer::RemoveSource(WtfAudio::Generator::tRefa rStream)
{
	mSourceList.remove(rStream);
};

/**
 * This multiplexer takes the average of its input waveforms
 **/
void Multiplexer::AdditiveType::CombineStreams(WtfEngine::NonCopyBuffer<>::tRefa rOutput,
	const Multiplexer::tInputBufferList& inputs, unsigned uOutputRate)
{
	tInternalFormat * pOutput = ((tInternalFormat *)rOutput->getData()), * pInput;
	tInternalFormat denom = (tInternalFormat)inputs.size();
	
	for(unsigned i = 0; i < uOutputRate; i++) {
		pOutput[i] = 0;
	};

	// TODO: handle differing sample rates
	for(Multiplexer::tInputBufferList::const_iterator itr = inputs.begin();
		itr != inputs.end(); itr++)
	{
		pInput = (tInternalFormat *)itr->first->getData();
		for(unsigned i = 0; i < uOutputRate; i++) {
			pOutput[i] += pInput[i];
		};
	};
	
	if(denom != 0) {
		for(unsigned i = 0; i < uOutputRate; i++) {
			pOutput[i] /= denom;
		};
	};
};

tScalar SineWaveFunction::getValue(tScalar time, tScalar dt)
{
	return sin( (2.0 * M_PI * mrSequencer->getValue(time, dt)) * time );
};

tScalar SquareWaveFunction::getValue(tScalar time, tScalar dt)
{
	float g = 1/mrSequencer->getValue(time, dt);
	return (fmod(time, g) > 0.5*g) ? 1 : -1;
}